//
// Created by vell0 on 2022/4/24.
//

#ifndef ZG_SLAM_VTCPCLIENT_H
#define ZG_SLAM_VTCPCLIENT_H

#include "VLog.h"
#include <arpa/inet.h>
#include <sys/socket.h>
#include "VThreadHandler.h"
#include "VTimeUtils.h"
#include "VTcpClient.h"
#include "VFileUtils.h"

namespace VUtils {
    class VTcpClient {
    private:
        std::string host;
        int port;
        int sock = -1;
        int retryIntervalMs = 1000; // 断线重连间隔时间
        VUtils::VThreadHandler connectThread;
        bool isConnecting = false;
        bool connected = false;
    public:
        VTcpClient(const std::string &host, int port) : host(host), port(port) {}

        VTcpClient(const std::string &host, int port, int retryIntervalMs) : host(host), port(port),
                                                                             retryIntervalMs(retryIntervalMs) {}

        VTcpClient() {}

        void setHost(const std::string &host, int port) {
            this->host = host;
            this->port = port;
        }

        int _connect() {
            //创建套接字
            sock = socket(AF_INET, SOCK_STREAM, 0);
            if (sock < 0) {
                VLog("connect error %d", sock);
                return sock;
            }
            //向服务器（特定的IP和端口）发起请求
            struct sockaddr_in serv_addr;
            memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
            serv_addr.sin_family = AF_INET;  //使用IPv4地址
            serv_addr.sin_addr.s_addr = inet_addr(host.c_str());  //具体的IP地址
            serv_addr.sin_port = htons(port);  //端口
            int code = connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
            if (code != 0) {
                VLog("connect error %d %s:%d", code, host.c_str(), port);
                return code;
            }
            connected = true;
            return 0;
        }

        int disconnect() {
            try {
                return close(sock);
            } catch (...) {
                return -1;
            }
            return 0;
        }

        /**
         * 开始连接，断线自动重连
         */
        void start() {
            if (isConnecting) {
                return;
            }
            isConnecting = true;
            connectThread.post([this]() {
                disconnect();
                while (_connect() != 0) {
                    VLog("reconnect after %d ms", retryIntervalMs)
                    VUtils::sleep(retryIntervalMs);
                }
                isConnecting = false;
            });
        }

        /**
         * 写入数据，写失败了不会重试
         * @param __buf
         * @param __n
         * @return
         */
        int write(const void *data, size_t size) {
            if (!connected) {
                return -1;
            }
            int code = send(sock, data, size, MSG_NOSIGNAL);
            if (code < 0) {
                connected = false;
                VLog("write error %d, try reconnect", code)
                start(); // 如果写入失败，尝试重新连接
            }
            return code;
        }

        /**
         * 读取固定大小数据
         * @param data
         * @param neededSize
         * @return
         */
        int read(uint8_t *data, size_t neededSize) {
            int code = recv(sock, data, neededSize, MSG_WAITALL | MSG_NOSIGNAL);
            if (code <= 0) {
                connected = false;
                VLog("read error %d, try reconnect", code)
                start(); // 如果写入失败，尝试重新连接
                VUtils::sleep(retryIntervalMs);
            }
            return code;
        }

        int read(uint8_t *data, size_t neededSize, int flag) {
            int code = recv(sock, data, neededSize, flag);
            if (code < 0) {
                connected = false;
                VLog("read error %d, try reconnect", code)
                start(); // 如果写入失败，尝试重新连接
                VUtils::sleep(retryIntervalMs);
            }
            return code;
        }
    };
}
#endif //ZG_SLAM_VTCPCLIENT_H
